/*
 * AP_MavlinkCommand.cpp
 *
 *  Created on: Apr 30, 2011
 *      Author: jgoppert
 */

#include "AP_MavlinkCommand.h"

namespace apo {

AP_MavlinkCommand::AP_MavlinkCommand(const AP_MavlinkCommand & v) :
	_data(v._data), _seq(v._seq) {
	//if (FastSerial::getInitialized(0)) Serial.println("copy ctor");
}

AP_MavlinkCommand::AP_MavlinkCommand(uint16_t index, bool doLoad) :
	_data(k_commands + index), _seq(index) {

	if (FastSerial::getInitialized(0)) {
		Serial.println("index ctor");
		Serial.println("++");
		Serial.print("index: "); Serial.println(index);
		Serial.print("key: "); Serial.println(k_commands + index);
		Serial.println("++");
	}

	// default values for structure
	_data.get().command = MAV_CMD_NAV_WAYPOINT;
	_data.get().autocontinue = true;
	_data.get().frame = MAV_FRAME_GLOBAL;
	_data.get().param1 = 0;
	_data.get().param2 = 10; // radius of 10 meters
	_data.get().param3 = 0;
	_data.get().param4 = 0;
	_data.get().x = 0;
	_data.get().y = 0;
	_data.get().z = 1000;

	// This is a failsafe measure to stop trying to load a command if it can't load
	if (doLoad && !load()) {
		Serial.println("load failed, reverting to home waypoint");
		_data = AP_MavlinkCommand::home._data;
		_seq = AP_MavlinkCommand::home._seq;
	}
}

AP_MavlinkCommand::AP_MavlinkCommand(const mavlink_waypoint_t & cmd) :
	_data(k_commands + cmd.seq), _seq(cmd.seq) {
	setCommand(MAV_CMD(cmd.command));
	setAutocontinue(cmd.autocontinue);
	setFrame(MAV_FRAME(cmd.frame));
	setParam1(cmd.param1);
	setParam2(cmd.param2);
	setParam3(cmd.param3);
	setParam4(cmd.param4);
	setX(cmd.x);
	setY(cmd.y);
	setZ(cmd.z);
	save();

	// reload home if sent
	if (cmd.seq == 0) home.load();

	Serial.println("============================================================");
	Serial.println("storing new command from mavlink_waypoint_t");
	Serial.print("key: "); Serial.println(_data.key(),DEC);
	Serial.print("number: "); Serial.println(cmd.seq,DEC);
	Serial.print("command: "); Serial.println(getCommand());
	Serial.print("autocontinue: "); Serial.println(getAutocontinue(),DEC);
	Serial.print("frame: "); Serial.println(getFrame(),DEC);
	Serial.print("1000*param1: "); Serial.println(int(1000*getParam1()),DEC);
	Serial.print("1000*param2: "); Serial.println(int(1000*getParam2()),DEC);
	Serial.print("1000*param3: "); Serial.println(int(1000*getParam3()),DEC);
	Serial.print("1000*param4: "); Serial.println(int(1000*getParam4()),DEC);
	Serial.print("1000*x0: "); Serial.println(int(1000*cmd.x),DEC);
	Serial.print("1000*y0: "); Serial.println(int(1000*cmd.y),DEC);
	Serial.print("1000*z0: "); Serial.println(int(1000*cmd.z),DEC);
	Serial.print("1000*x: "); Serial.println(int(1000*getX()),DEC);
	Serial.print("1000*y: "); Serial.println(int(1000*getY()),DEC);
	Serial.print("1000*z: "); Serial.println(int(1000*getZ()),DEC);

	load();

	Serial.print("1000*x1: "); Serial.println(int(1000*getX()),DEC);
	Serial.print("1000*y1: "); Serial.println(int(1000*getY()),DEC);
	Serial.print("1000*z1: "); Serial.println(int(1000*getZ()),DEC);
	Serial.println("============================================================");
	Serial.flush();
}

mavlink_waypoint_t AP_MavlinkCommand::convert(uint8_t current) const {
	mavlink_waypoint_t mavCmd;
	mavCmd.seq = getSeq();
	mavCmd.command = getCommand();
	mavCmd.frame = getFrame();
	mavCmd.param1 = getParam1();
	mavCmd.param2 = getParam2();
	mavCmd.param3 = getParam3();
	mavCmd.param4 = getParam4();
	mavCmd.x = getX();
	mavCmd.y = getY();
	mavCmd.z = getZ();
	mavCmd.autocontinue = getAutocontinue();
	mavCmd.current = (getSeq() == current);
	mavCmd.target_component = mavlink_system.compid;
	mavCmd.target_system = mavlink_system.sysid;
	return mavCmd;
}

float AP_MavlinkCommand::bearingTo(const AP_MavlinkCommand & next) const {
	float deltaLon = next.getLon() - getLon();
	/*
	 Serial.print("Lon: "); Serial.println(getLon());
	 Serial.print("nextLon: "); Serial.println(next.getLon());
	 Serial.print("deltaLonDeg * 1e7: "); Serial.println(deltaLon*rad2DegInt);
	 */
	float bearing = atan2(
			sin(deltaLon) * cos(next.getLat()),
			cos(getLat()) * sin(next.getLat()) - sin(getLat()) * cos(
					next.getLat()) * cos(deltaLon));
	return bearing;
}

float AP_MavlinkCommand::bearingTo(int32_t latDegInt, int32_t lonDegInt) const {
	// have to be careful to maintain the precision of the gps coordinate
	float deltaLon = (lonDegInt - getLon_degInt()) * degInt2Rad;
	float nextLat = latDegInt * degInt2Rad;
	float bearing = atan2(
			sin(deltaLon) * cos(nextLat),
			cos(getLat()) * sin(nextLat) - sin(getLat()) * cos(nextLat)
					* cos(deltaLon));
	if (bearing < 0)
		bearing += 2 * M_PI;
	return bearing;
}

float AP_MavlinkCommand::distanceTo(const AP_MavlinkCommand & next) const {
	float sinDeltaLat2 = sin((getLat() - next.getLat()) / 2);
	float sinDeltaLon2 = sin((getLon() - next.getLon()) / 2);
	float a = sinDeltaLat2 * sinDeltaLat2 + cos(getLat()) * cos(
			next.getLat()) * sinDeltaLon2 * sinDeltaLon2;
	float c = 2 * atan2(sqrt(a), sqrt(1 - a));
	return rEarth * c;
}

float AP_MavlinkCommand::distanceTo(int32_t lat_degInt, int32_t lon_degInt) const {
	float sinDeltaLat2 = sin(
			(lat_degInt - getLat_degInt()) * degInt2Rad / 2);
	float sinDeltaLon2 = sin(
			(lon_degInt - getLon_degInt()) * degInt2Rad / 2);
	float a = sinDeltaLat2 * sinDeltaLat2 + cos(getLat()) * cos(
			lat_degInt * degInt2Rad) * sinDeltaLon2 * sinDeltaLon2;
	float c = 2 * atan2(sqrt(a), sqrt(1 - a));
	/*
	 Serial.print("wp lat_degInt: "); Serial.println(getLat_degInt());
	 Serial.print("wp lon_degInt: "); Serial.println(getLon_degInt());
	 Serial.print("lat_degInt: "); Serial.println(lat_degInt);
	 Serial.print("lon_degInt: "); Serial.println(lon_degInt);
	 Serial.print("sinDeltaLat2: "); Serial.println(sinDeltaLat2);
	 Serial.print("sinDeltaLon2: "); Serial.println(sinDeltaLon2);
	 */
	return rEarth * c;
}

//calculates cross track of a current location
float AP_MavlinkCommand::crossTrack(const AP_MavlinkCommand & previous,
		int32_t lat_degInt, int32_t lon_degInt) const {
	float d = previous.distanceTo(lat_degInt, lon_degInt);
	float bCurrent = previous.bearingTo(lat_degInt, lon_degInt);
	float bNext = previous.bearingTo(*this);
	return asin(sin(d / rEarth) * sin(bCurrent - bNext)) * rEarth;
}

// calculates along  track distance of a current location
float AP_MavlinkCommand::alongTrack(const AP_MavlinkCommand & previous,
		int32_t lat_degInt, int32_t lon_degInt) const {
	// ignores lat/lon since single prec.
	float dXt = this->crossTrack(previous,lat_degInt, lon_degInt);
	float d = previous.distanceTo(lat_degInt, lon_degInt);
	return dXt / tan(asin(dXt / d));
}


AP_MavlinkCommand AP_MavlinkCommand::home = AP_MavlinkCommand(0,false);

}
